package com.unitd.frame.mybatis.codegen;

import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.config.CommentGeneratorConfiguration;
import org.mybatis.generator.config.Context;
import org.mybatis.generator.internal.types.JavaTypeResolverDefaultImpl;
import org.mybatis.generator.internal.util.StringUtility;

import java.util.*;

/**
 * @desc 利用MyBatis自动生成增删改查语句的插件
 * @filename CrudSupportPlugin.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2017/2/5
 */
public class CrudSupportPlugin extends PluginAdapter {
	private Set<String> mappers = new HashSet<>();
	private boolean caseSensitive = false;

	/** 开始的分隔符，例如mysql为`，sqlserver为[ */
	private String beginningDelimiter = "";
	/** 结束的分隔符，例如mysql为`，sqlserver为] */
	private String endingDelimiter = "";
	/** 数据库模式 */
	private String schema;
	 /** 注释生成器 */
	private CommentGeneratorConfiguration commentCfg;
	/** Java数据类型分解器 */
	private JavaTypeResolverDefaultImpl javaTypeResolver = new JavaTypeResolverDefaultImpl();

	/**
	 * @desc 设置代码自动生成的上下文对象信息和基本参数信息
	 * @param context 上下文对象
	 */
	@Override
	public void setContext(Context context) {
		super.setContext(context);
		// 设置默认的注释生成器
		commentCfg = new CommentGeneratorConfiguration();
		commentCfg.setConfigurationType(MyCommentGenerator.class.getCanonicalName());
		context.setCommentGeneratorConfiguration(commentCfg);
		// 支持oracle获取注释#114
		context.getJdbcConnectionConfiguration().addProperty("remarksReporting", "true");
	}

	/**
	 * @desc 设置代码自动生成的一些基础参数信息
	 * @param properties 属性配置信息
	 */
	@Override
	public void setProperties(Properties properties) {
		super.setProperties(properties);
		String mappers = this.properties.getProperty("mappers");
		if (StringUtility.stringHasValue(mappers)) {
			this.mappers.addAll(Arrays.asList(mappers.split(",")));
		} else {
			throw new RuntimeException("Mapper插件缺少必要的mappers属性!");
		}
		String caseSensitive = this.properties.getProperty("caseSensitive");
		if (StringUtility.stringHasValue(caseSensitive)) {
			this.caseSensitive = caseSensitive.equalsIgnoreCase("TRUE");
		}
		String beginningDelimiter = this.properties.getProperty("beginningDelimiter");
		if (StringUtility.stringHasValue(beginningDelimiter)) {
			this.beginningDelimiter = beginningDelimiter;
		}
		commentCfg.addProperty("beginningDelimiter", this.beginningDelimiter);
		String endingDelimiter = this.properties.getProperty("endingDelimiter");
		if (StringUtility.stringHasValue(endingDelimiter)) {
			this.endingDelimiter = endingDelimiter;
		}
		commentCfg.addProperty("endingDelimiter", this.endingDelimiter);
		String schema = this.properties.getProperty("schema");
		if (StringUtility.stringHasValue(schema)) {
			this.schema = schema;
		}
	}

	/**
	 * @desc 根据数据库相应的分隔符,重新包装属性
	 * @param name 属性
	 * @return
	 */
	public String getDelimiterName(String name) {
		StringBuilder nameBuilder = new StringBuilder();
		if (StringUtility.stringHasValue(schema)) {
			nameBuilder.append(schema);
			nameBuilder.append(".");
		}
		nameBuilder.append(beginningDelimiter);
		nameBuilder.append(name);
		nameBuilder.append(endingDelimiter);
		return nameBuilder.toString();
	}

	@Override
	public boolean validate(List<String> warnings) {
		return true;
	}

	/**
	 * @desc 生成的Mapper接口
	 * @param interfaze 生成的mapper接口对象
	 * @param topLevelClass 接口类
	 * @param introspectedTable 对应的表
	 * @return boolean
	 */
	@Override
	public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		// 获取实体类
		FullyQualifiedJavaType entityType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());
		// 主键
		FullyQualifiedJavaType idType = null;
		List<IntrospectedColumn> columns = introspectedTable.getPrimaryKeyColumns();
		for (IntrospectedColumn col : columns) {
			idType = javaTypeResolver.calculateJavaType(col);
			break;
		}
		// import接口
		for (String mapper : mappers) {
			// import实体类
			interfaze.addImportedType(new FullyQualifiedJavaType(mapper));
			// 添加类似 extends IBaseDao<?, ?> 语句
			interfaze.addSuperInterface(new FullyQualifiedJavaType(mapper + "<" + entityType.getShortName() + "," + idType.getShortName() + ">"));
		}

		// import实体类
		interfaze.addImportedType(entityType);
		return true;
	}

	/**
	 * @desc 处理实体类的包和@Table注解
	 * @param topLevelClass 实体对象
	 * @param introspectedTable 对应的表
	 */
	private void processEntityClass(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		// 引入JPA注解
		topLevelClass.addImportedType("javax.persistence.*");
		// 引入lombok注解
		topLevelClass.addImportedType("lombok.AllArgsConstructor");
		topLevelClass.addImportedType("lombok.Data");
		topLevelClass.addImportedType("lombok.NoArgsConstructor");

		String tableName = introspectedTable.getFullyQualifiedTableNameAtRuntime();
		// 如果包含空格，或者需要分隔符，需要完善
		if (StringUtility.stringContainsSpace(tableName)) {
			tableName = context.getBeginningDelimiter()
					+ tableName
					+ context.getEndingDelimiter();
		}

		// 是否忽略大小写，对于区分大小写的数据库，会有用
		if (caseSensitive && !topLevelClass.getType().getShortName().equals(tableName)) {
			topLevelClass.addAnnotation("@Data");
			topLevelClass.addAnnotation("@NoArgsConstructor");
			topLevelClass.addAnnotation("@AllArgsConstructor");
			topLevelClass.addAnnotation("@Table(name = \"" + getDelimiterName(tableName.toUpperCase()) + "\")");
		} else if (!topLevelClass.getType().getShortName().equalsIgnoreCase(tableName)) {
			topLevelClass.addAnnotation("@Data");
			topLevelClass.addAnnotation("@NoArgsConstructor");
			topLevelClass.addAnnotation("@AllArgsConstructor");
			topLevelClass.addAnnotation("@Table(name = \"" + getDelimiterName(tableName.toUpperCase()) + "\")");
		} else if (StringUtility.stringHasValue(schema)
				|| StringUtility.stringHasValue(beginningDelimiter)
				|| StringUtility.stringHasValue(endingDelimiter)) {
			topLevelClass.addAnnotation("@Data");
			topLevelClass.addAnnotation("@NoArgsConstructor");
			topLevelClass.addAnnotation("@AllArgsConstructor");
			topLevelClass.addAnnotation("@Table(name = \"" + getDelimiterName(tableName.toUpperCase()) + "\")");
		}
	}

	/**
	 * @desc 生成基础实体类
	 * @param topLevelClass 基础实体类
	 * @param introspectedTable 对应的表
	 * @return boolean
	 */
	@Override
	public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		processEntityClass(topLevelClass, introspectedTable);
		return true;
	}

	/**
	 * @desc 生成实体类注解KEY对象
	 * @param topLevelClass 基础实体类
	 * @param introspectedTable 对应的表
	 * @return boolean
	 */
	@Override
	public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		processEntityClass(topLevelClass, introspectedTable);
		return true;
	}

	/**
	 * @desc 生成带BLOB字段的对象
	 * @param topLevelClass 基础实体类
	 * @param introspectedTable 对应的表
	 * @return boolean
	 */
	@Override
	public boolean modelRecordWithBLOBsClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		processEntityClass(topLevelClass, introspectedTable);
		return false;
	}

	/**
	 * @desc 生成Getter方法,该方法在生成每一个属性的getter方法时候调用，如果我们不想生成getter，直接返回false即可
	 * @param method getter方法名
	 * @param topLevelClass 基础实体类
	 * @param introspectedColumn 对应的属性
	 * @param introspectedTable 对应的表
	 * @param modelClassType 对应的实体类类型
	 * @return
	 */
	@Override
	public boolean modelGetterMethodGenerated(Method method,
	                                          TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn,
	                                          IntrospectedTable introspectedTable,
	                                          ModelClassType modelClassType) {
		return false;
	}

	/**
	 * @desc 生成Getter方法,该方法在生成每一个属性的setter方法时候调用，如果我们不想生成setter，直接返回false即可
	 * @param method setter方法名
	 * @param topLevelClass 基础实体类
	 * @param introspectedColumn 对应的属性
	 * @param introspectedTable 对应的表
	 * @param modelClassType 对应的实体类类型
	 * @return
	 */
	@Override
	public boolean modelSetterMethodGenerated(Method method,
	                                          TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn,
	                                          IntrospectedTable introspectedTable,
	                                          ModelClassType modelClassType) {
		return false;
	}

	/****** !!!!!! 下面所有return false的方法都不生成。这些都是基础的CRUD方法，使用通用Mapper实现 !!!!!! ******/
	@Override
	public boolean clientDeleteByPrimaryKeyMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientInsertMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientInsertSelectiveMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientSelectByPrimaryKeyMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientUpdateByPrimaryKeySelectiveMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientUpdateByPrimaryKeyWithBLOBsMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientUpdateByPrimaryKeyWithoutBLOBsMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientDeleteByPrimaryKeyMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientInsertMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientInsertSelectiveMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientSelectAllMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientSelectAllMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientSelectByPrimaryKeyMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientUpdateByPrimaryKeySelectiveMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientUpdateByPrimaryKeyWithBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean clientUpdateByPrimaryKeyWithoutBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean sqlMapDeleteByPrimaryKeyElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean sqlMapInsertElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean sqlMapInsertSelectiveElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean sqlMapSelectAllElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean sqlMapSelectByPrimaryKeyElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean sqlMapUpdateByPrimaryKeySelectiveElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean sqlMapUpdateByPrimaryKeyWithBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean sqlMapUpdateByPrimaryKeyWithoutBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean providerGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean providerApplyWhereMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean providerInsertSelectiveMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		return false;
	}

	@Override
	public boolean providerUpdateByPrimaryKeySelectiveMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		return false;
	}
}